
package com.rem.shiro.config;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Base64Utils;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Rem
 * @date 2021-04-06
 */


@Configuration
public class ShiroConfig {

    @Autowired
    RedisProperties redisProperties;


    /**
     * 创建shiroFilter  负责拦截所以请求
     *
     * @param defaultSecurityManager
     * @return
     */


    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager defaultSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultSecurityManager);

        //配置系统受限资源
        //配置系统公共资源
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        //authc 表示请求的资源需要认证和授权


        map.put("/register.jsp", "anon");
        map.put("/user/login", "anon");
        map.put("/user/register", "anon");
        map.put("/user/getImage", "anon");
        map.put("/jwt/**", "anon");
        map.put("/**", "authc");


        System.out.println("拦截生效");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        //默认认证界面
        shiroFilterFactoryBean.setLoginUrl("/login.jsp");

        return shiroFilterFactoryBean;
    }


    /**
     * 创建安全管理器
     * 配置认证授权功能
     *
     * @return
     */


    @Bean
    public SecurityManager getDefaultWebSecurityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //注入realm
        securityManager.setRealm(getShiroRealm());
        // 配置 shiro session管理器
        securityManager.setSessionManager(sessionManager());
        // 配置 缓存管理类 cacheManager
        securityManager.setCacheManager(cacheManager());
        // 配置 rememberMeCookie
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }


    @Bean
    public Realm getShiroRealm() {
        ShiroRealm shiroRealm = new ShiroRealm();
        //给realm 设置凭证校验匹配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        //设置加密
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        hashedCredentialsMatcher.setHashIterations(1024);
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);

        //设置本地缓存
        //  localCache(shiroRealm);

        return shiroRealm;
    }


    /**
     * 本地缓存
     *
     * @param shiroRealm *
     */


    private void localCache(ShiroRealm shiroRealm) {
        shiroRealm.setCacheManager(new EhCacheManager());
        //开启缓存
        shiroRealm.setCachingEnabled(true);
        //开启认证缓存
        shiroRealm.setAuthenticationCachingEnabled(true);
        //设置认证缓存名称 可以不设
        shiroRealm.setAuthenticationCacheName("AuthenticationCache 认证缓存");
        //开启授权缓存
        shiroRealm.setAuthorizationCachingEnabled(true);
        //设置授权缓存名称 可以不设
        shiroRealm.setAuthorizationCacheName("AuthorizationCache 授权缓存");
    }


//===================================缓存=======================================


    /**
     * shiro 中配置 redis 缓存
     *
     * @return RedisManager
     */


    private RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();

        redisManager.setHost(redisProperties.getHost() + ":" + redisProperties.getPort());
        if (StringUtils.isNotBlank(redisProperties.getPassword())) {
            redisManager.setPassword(redisProperties.getPassword());
        }
        redisManager.setTimeout(3600);
        redisManager.setDatabase(redisProperties.getDatabase());
        return redisManager;
    }


    /**
     * redisCache
     *
     * @return
     */

    private RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }


    /**
     * redisSessionDAO
     *
     * @return *
     */


    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }


//===================================session=======================================


    /**
     * session 管理对象
     *
     * @return DefaultWebSessionManager
     * *  *
     */


    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        Collection<SessionListener> listeners = new ArrayList<>();
        listeners.add(new ShiroSessionListener());
        // 设置 session超时时间
        sessionManager.setGlobalSessionTimeout(3600 * 1000L);
        sessionManager.setSessionListeners(listeners);
        sessionManager.setSessionDAO(redisSessionDAO());
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    static class ShiroSessionListener implements SessionListener {

        private final AtomicInteger sessionCount = new AtomicInteger(0);

        @Override
        public void onStart(Session session) {
            sessionCount.incrementAndGet();
        }

        @Override
        public void onStop(Session session) {
            sessionCount.decrementAndGet();
        }

        @Override
        public void onExpiration(Session session) {
            sessionCount.decrementAndGet();
        }
    }


//===================================记住我=======================================

    /**
     * rememberMe cookie 效果是重开浏览器后无需重新登录
     *
     * @return SimpleCookie
     * *
     */


    private SimpleCookie rememberMeCookie() {
        // 设置 cookie 名称，对应 login.html 页面的 <input type="checkbox" name="rememberMe"/>
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        // 设置 cookie 的过期时间，单位为秒，这里为一天
        cookie.setMaxAge(86400);
        cookie.setSecure(true);
        //前端拿不到cookie
        cookie.setHttpOnly(true);
        return cookie;
    }

    /**
     * cookie管理对象
     *
     * @return CookieRememberMeManager
     * *
     */


    private CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        // rememberMe cookie 加密的密钥
        String encryptKey = "Ao9jPiVREg6ROr3mJeFK7WlRBwudX";
        byte[] encryptKeyBytes = encryptKey.getBytes(StandardCharsets.UTF_8);
        String rememberKey = Base64Utils.encodeToString(Arrays.copyOf(encryptKeyBytes, 16));
        cookieRememberMeManager.setCipherKey(Base64.decode(rememberKey));
        return cookieRememberMeManager;
    }


    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


}


